# DBSCAN

"""
DBSCAN 是一种基于密度的聚类算法，主要优点是不需要用户提前指定簇的数量，能够识别复杂形状的簇，并且可以识别不属于任何簇的噪声点。它比凝聚聚类和 k 均值稍慢，但仍能处理较大的数据集。
DBSCAN 的原理：
    核心点（Core Sample）：如果一个点在半径为 eps 的范围内至少有 min_samples 个点，那么这个点是核心点。
    边界点（Boundary Point）：与核心点距离在 eps 范围内，但自身不是核心点。
    噪声点（Noise）：既不是核心点也不是边界点的点。
算法过程：
    从任意一个点开始，查找其 eps 范围内的所有点。
    如果这些点的数量小于 min_samples，则该点被标记为噪声。
    如果大于等于 min_samples，则该点被标记为核心点，并分配一个新的簇标签。
    接着，访问该点的所有邻居（在 eps 范围内）。如果邻居未被分配簇，则分配相同的簇标签；如果是核心点，则递归访问其邻居。
    簇逐渐扩大，直到没有更多核心点可以加入。
    选择下一个未访问的点，重复上述过程。
结果：
    核心点：始终被分配到相同的簇。
    边界点：可能与多个簇的核心点相邻，其归属依赖于访问顺序。
    噪声点：始终被标记为噪声。
总体来说，DBSCAN 对核心点和噪声点的分类是稳定的，但边界点的分类可能因访问顺序而略有不同。不过，这种情况很少发生，对整体聚类结果影响不大。
"""
import mglearn
from matplotlib import pyplot as plt
# 我们将 DBSCAN 应用于演示凝聚聚类的模拟数据集。与凝聚聚类类似，DBSCAN 也不允
# 许对新的测试数据进行预测，所以我们将使用 fit_predict 方法来执行聚类并返回簇标签。

from sklearn.cluster import DBSCAN
from sklearn.datasets import make_blobs, make_moons
from sklearn.preprocessing import StandardScaler

X,y = make_blobs(random_state=0, n_samples=12)
dbscan = DBSCAN()
clusters = dbscan.fit_predict(X)
print("Cluster memberships:\n{}".format(clusters))
# [-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1]

'''
所有数据点都被分配了标签 -1，表示它们都被识别为噪声。
这是因为在默认的 eps 和 min_samples 参数设置下，DBSCAN 无法识别出足够密集的区域来形成簇。
对于小型玩具数据集，如果不调整这些参数，很容易出现这种情况。
'''

mglearn.plots.plot_dbscan()
plt.show()

'''
图的说明：
    实心点：属于簇的点。
    空心点：噪声点。
    大标记：核心样本。
    小标记：边界点。
参数调整的影响：
    1.增大 eps（从左到右）：
        更多的点会被包含在同一个簇中，簇会变大。
        可能导致多个簇合并成一个簇。
            eps 决定点与点之间的“接近”程度，非常关键。
                如果 eps 太小，几乎没有点会被识别为核心样本，所有点都可能被标记为噪声。
                如果 eps 太大，所有点可能会合并成一个大簇。
    2.增大 min_samples（从上到下）：
        核心点会变少，更多的点被标记为噪声。
            min_samples 决定簇的最小规模，即一个簇至少需要包含 min_samples 个点。
        如果 min_samples 增大，小簇会被标记为噪声，只有足够大的簇才能被保留。
        例如，在 eps=1.5 时：
            min_samples=3：有三个簇，分别包含 4、5 和 3 个点。
            min_samples=5：只有包含 5 个点的簇被保留，其他两个小簇被标记为噪声。
参数调整的建议：
    虽然 DBSCAN 不需要显式设置簇的数量，但通过调整 eps 可以隐式控制簇的数量。
    使用 StandardScaler 或 MinMaxScaler 对数据进行缩放后，更容易找到合适的 eps 值，因为缩放后的数据特征范围更接近，使距离计算更合理。
总之，eps 和 min_samples 的选择对 DBSCAN 的聚类结果至关重要，需要根据具体数据集进行调整。
'''

# 下面展示了在 two_moons 数据集上运行 DBSCAN 的结果。利用默认设置，算法找到了两个半圆形并将其分开：

X,y = make_moons(n_samples=200,noise=0.05,random_state=0)
scaler = StandardScaler()
scaler.fit(X)
X_scaled = scaler.transform(X)

dbscan = DBSCAN()
clusters = dbscan.fit_predict(X_scaled)

plt.scatter(X_scaled[:,0],X_scaled[:,1],c=clusters,cmap=mglearn.cm2,s=60)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.show()

'''
参数设置对 DBSCAN 的结果影响很大。例如：
    当 eps 设置为合适的值时，算法找到了我们期望的 2 个簇，效果很好。
    如果将 eps 减小到 0.2（默认值是 0.5），会得到 8 个簇，这显然太多了。
    如果将 eps 增大到 0.7，则所有点会合并成一个簇。
注意事项：
在使用 DBSCAN 时，需要特别注意返回的簇分配结果。因为噪声点的标签是 -1，如果用簇标签对其他数据进行索引，可能会导致意外的结果。所以，在处理簇标签时，要小心处理这些噪声点。
'''


'''
k均值聚类
优点：
    簇的表示直观：可以用簇的平均值来表示簇，每个数据点都由其簇中心表示，簇中心作为簇的代表点，能较好地反映簇内数据的集中趋势，便于理解和解释聚类结果。
    易于理解和实现：算法原理简单，实现起来也比较容易，对于初学者来说比较友好，便于快速上手进行聚类分析。
    可指定簇的数量：能够根据实际需求明确指定想要划分的簇的数量，这在一些对簇数量有明确要求的场景下非常有用，比如将客户分为特定数量的群体以便进行针对性的营销策略制定等。
缺点：
    对初始中心的选择敏感：初始簇中心的选择可能会对最终的聚类结果产生较大影响，不同的初始中心可能导致不同的聚类结果，需要多次尝试或采用一些优化方法来选择初始中心。
    对异常值敏感：由于是基于均值来计算簇中心，异常值可能会对簇中心的计算产生较大干扰，从而影响聚类的准确性，导致一些正常的数据点被错误地划分到其他簇中。
    只能发现凸形簇：难以发现非凸形状的簇，对于一些具有复杂形状的数据分布，如环形、螺旋形等，k均值聚类可能无法很好地识别和划分簇。

DBSCAN聚类
优点：
    能检测噪声点：可以识别出不属于任何簇的噪声点，这对于数据中存在大量异常值或噪声数据的情况非常有帮助，能够有效区分正常数据和噪声数据，提高聚类的质量和可靠性。
    自动判断簇数量：不需要事先指定簇的数量，算法会根据数据的分布和eps参数自动确定簇的数量，更加灵活地适应不同的数据集，减少了人为设定参数的困难。
    簇形状灵活：允许簇具有复杂的形状，能够很好地处理非凸形状的簇，如在“two_moons”数据集这种具有特殊形状的数据中，DBSCAN能够准确地识别出两个月牙形的簇，这是其独特的优势。
缺点：
    参数选择困难：eps和min_samples两个参数的选择对聚类结果影响很大，但很难确定最佳参数值，不同的数据集需要不同的参数组合，需要多次尝试和调整，增加了使用难度。
    簇大小差异大：有时会生成大小差别很大的簇，这在某些应用场景下可能不符合实际需求，比如在对不同规模的群体进行分析时，簇大小差异过大可能会导致对某些小簇的忽视或对大簇的过度关注，从而影响对整体数据的全面理解。
    对密度差异大的数据效果不佳：当数据集中不同区域的密度差异较大时，很难找到一组合适的参数来同时适用于高密度和低密度区域，可能导致聚类结果不理想，部分数据无法正确归入簇中或被错误地划分为噪声点。

凝聚聚类
优点：
    提供层次结构：可以提供数据的可能划分的整个层次结构，通过树状图（dendrogram）能够直观地展示数据从最细粒度到最粗粒度的聚类过程，便于用户根据不同的需求选择合适的聚类粒度，深入了解数据的内在结构和关系。
    可指定簇的数量：和k均值类似，用户可以根据实际情况在层次结构中选择合适的截断点来确定簇的数量，具有较好的灵活性，能够满足不同场景下对簇数量的要求。
    适用于不同形状的簇：相比k均值，凝聚聚类对簇的形状要求不那么严格，能够更好地处理一些非凸形状的簇，对于复杂的数据分布也能有较好的聚类效果。
缺点：
    计算复杂度较高：对于大型数据集，计算距离矩阵和不断合并簇的过程会消耗大量的计算资源和时间，算法的效率较低，可能会导致在实际应用中处理速度较慢，难以满足实时性要求较高的场景。
    难以处理噪声数据：在聚类过程中，噪声数据可能会对簇的合并产生干扰，导致一些不合理的簇合并，影响最终的聚类结果，需要在聚类前对数据进行预处理以去除噪声，增加了数据处理的复杂性。
    选择截断点较为主观：虽然可以通过树状图来选择簇的数量，但截断点的选择仍然具有一定的主观性，不同的用户可能会根据自己的理解和需求选择不同的截断点，从而得到不同的聚类结果，缺乏一种客观的标准来确定最佳截断点。
'''


